{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Seria Lab: add a new ship\n",
    "This is a legacy demostration with obsolete seria API which previously built upon `treelib` library.  \n",
    "Experiment and understand the structure of the seria file. Demonstrate the use of seria module to parse and modifying the seria file"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import seria\n",
    "\n",
    "profile_tree = seria.load('sample/profile.seria')\n",
    "\n",
    "# design_ship_tree = seria.load('sample/Courageous.seria')\n",
    "# design_ship_tree = seria.load('sample/Meteor Mk2.seria')\n",
    "design_ship_tree = seria.load('sample/Rook.seria')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Add a new ship to an existing fleet\n",
    "\n",
    "A ship in the profile has exactly the same structure (nodes) compare to a ship design file. This means we can easily copy the data from a design file and paste it into the profile. However, you need to change relavent id and add additional ids, in order to make it working correctly. Failing to do so will cause the game to crash."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "id count: 2959\n",
      "unique id count: 2959\n"
     ]
    }
   ],
   "source": [
    "# m_id are unique\n",
    "ids = list(map(int, profile_tree.get_children_attribute('m_id')))\n",
    "\n",
    "unique_ids = set(ids)\n",
    "\n",
    "print('id count:', len(ids))\n",
    "print('unique id count:', len(unique_ids))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "difference of root Node node:\n",
      "  m_master_id\n",
      "  m_state\n",
      "difference of Creature node:\n",
      "  m_alignment\n",
      "  m_bio_snapshot\n",
      "  m_escadra.id\n",
      "  m_escadra_index\n",
      "  m_moral\n",
      "  m_rad_seekness_timer\n",
      "  m_radiation_extra\n",
      "  m_rest\n",
      "  m_tarkhan\n",
      "  m_tele_crew_total\n",
      "  shipLoadTime\n",
      "  wasAddedToPlayerEscadra\n"
     ]
    }
   ],
   "source": [
    "def attribute_diff(a, b):\n",
    "    return set(a) - set(b)\n",
    "\n",
    "\n",
    "# attribute difference between in-profile ship and ship design\n",
    "p_fleet_tree = profile_tree.get_subtree_if(\n",
    "    lambda node: node.get_attribute('m_name') == 'MARK')\n",
    "\n",
    "# assume having the ship Sevastopol\n",
    "p_ship_tree = p_fleet_tree.get_subtree_if(\n",
    "    lambda node: node.get_attribute('m_name') == 'Rook')\n",
    "\n",
    "print('difference of root Node node:')\n",
    "p_ship_attribute_names = p_ship_tree.get_attribute_names()\n",
    "design_ship_attribute_names = design_ship_tree.get_attribute_names()\n",
    "for name in sorted(attribute_diff(p_ship_attribute_names, design_ship_attribute_names)):\n",
    "    print(' ', name)\n",
    "\n",
    "print('difference of Creature node:')\n",
    "p_creature_tree = p_ship_tree.get_subtree_by_class(\n",
    "    'Frame').get_subtree_by_class('Body').get_subtree_by_class('Creature')\n",
    "design_creature_tree = design_ship_tree.get_subtree_by_class(\n",
    "    'Frame').get_subtree_by_class('Body').get_subtree_by_class('Creature')\n",
    "seva_creature_attribute_names = p_creature_tree.get_attribute_names()\n",
    "cour_creature_attribute_names = design_creature_tree.get_attribute_names()\n",
    "for name in sorted(attribute_diff(seva_creature_attribute_names, cour_creature_attribute_names)):\n",
    "    print(' ', name)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By looking at the result above, we know that some attribute is required for a ship in a fleet:\n",
    "- `m_master_id`\n",
    "- `m_state`\n",
    "- `m_alignment`\n",
    "- `m_escadra.id`\n",
    "- `m_escadra_index`\n",
    "- `m_moral`\n",
    "- `m_tarkhan`\n",
    "- `m_tele_crew_total`\n",
    "- `wasAddedToPlayerEscadra`\n",
    "\n",
    "Additionally, `creatureId` is also needed to be set for the Creature node inside the ship. `nextCreatureId` give the right value for the next one and will also be updated.\n",
    "\n",
    "Steps to config a new ship node:\n",
    "1. set `m_master_id` of the root Node node to its target fleet `m_id` \n",
    "2. generate a new `m_id` and set to the root node and update relevant id reference to new `m_id`\n",
    "3. generate new `m_id` for each Body node and update relevant id\n",
    "4. generate a new `m_id` for the Frame node and update relevant id\n",
    "5. generate a new `m_id` for the Creature node and update relevant id"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# make new id unique from existing ids\n",
    "# next_id = max(unique_ids) + 1\n",
    "\n",
    "# obtain correct attribute values from profile\n",
    "next_creature_id = profile_tree.get_attribute('nextCreatureId')\n",
    "\n",
    "p_fleet_tree = profile_tree.get_subtree_if(\n",
    "    lambda node: node.get_attribute('m_name') == 'MARK')\n",
    "\n",
    "alignment = p_fleet_tree.get_attribute('m_alignment')\n",
    "escadra_id = p_fleet_tree.get_attribute('m_id')\n",
    "\n",
    "# make new escadra index for the new ship\n",
    "escadra_indexes = set()\n",
    "for node in p_fleet_tree.all_nodes_itr():\n",
    "    if node.tag == 'Creature':\n",
    "        escadra_indexes.add(int(node.get_attribute('m_escadra_index')))\n",
    "\n",
    "design_ship_tree.set_attribute('_header', 'm_children=7')\n",
    "design_ship_tree.set_attribute('m_master_id', escadra_id)\n",
    "\n",
    "# update root Node node\n",
    "# design_ship_tree.update_attribute('m_id', str(next_id))\n",
    "design_ship_tree.set_attribute('m_state', '2')\n",
    "\n",
    "# update all depth 2 Body nodes\n",
    "# for tree in design_ship_tree.get_subtrees_by_class('Body'):\n",
    "#     next_id += 1\n",
    "#     previous_id = tree.get_attribute('m_id')\n",
    "#     tree.update_attribute('m_id', str(next_id))\n",
    "#     # update Joint nodes and Slot nodes\n",
    "#     for node in design_ship_tree.all_nodes_itr():\n",
    "#         node.update_attribute_by_value(previous_id, str(next_id))\n",
    "\n",
    "# update Frame node\n",
    "design_frame_tree = design_ship_tree.get_subtree_by_class('Frame')\n",
    "\n",
    "# next_id += 1\n",
    "# design_frame_tree.update_attribute('m_id', str(next_id))\n",
    "\n",
    "# update all child Body nodes in Frame node\n",
    "# for tree in design_frame_tree.get_subtrees_by_class('Body'):\n",
    "#     next_id += 1\n",
    "#     previous_id = tree.get_attribute('m_id')\n",
    "#     tree.update_attribute('m_id', str(next_id))\n",
    "#     for node in design_ship_tree.all_nodes_itr():\n",
    "#         node.update_attribute_by_value(previous_id, str(next_id))\n",
    "\n",
    "# update Creature node\n",
    "design_creature_tree = design_frame_tree.get_subtree_by_class('Body').get_subtree_by_class(\n",
    "    'Creature')\n",
    "\n",
    "# next_id += 1\n",
    "# design_creature_tree.update_attribute('m_id', str(next_id))\n",
    "\n",
    "design_creature_tree.set_attribute('creatureId', next_creature_id)\n",
    "profile_tree.set_attribute('nextCreatureId', str(int(next_creature_id) + 1))\n",
    "\n",
    "design_creature_tree.set_attribute('m_alignment', alignment)\n",
    "design_creature_tree.set_attribute('m_escadra.id', escadra_id)\n",
    "design_creature_tree.set_attribute(\n",
    "    'm_escadra.index', str(max(escadra_indexes) + 1))\n",
    "design_creature_tree.set_attribute('m_moral', '10')\n",
    "design_creature_tree.set_attribute('m_tarkhan', 'DAUD')\n",
    "\n",
    "crew_capacity = design_creature_tree.get_attribute('m_tele_crew_capacity')\n",
    "design_creature_tree.set_attribute('m_tele_crew_total', crew_capacity)\n",
    "design_creature_tree.set_attribute('m_tele_crew_capacity', crew_capacity)\n",
    "\n",
    "# design_creature_tree.set_attribute('shipLoadTime', '0')\n",
    "design_creature_tree.set_attribute('wasAddedToPlayerEscadra', 'true')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "# copy the ship tree to the profile\n",
    "\n",
    "profile_tree.paste(p_fleet_tree.root, design_ship_tree)\n",
    "\n",
    "file = open('profile.seria', 'w', encoding='cp1251')\n",
    "file.write(seria.dump(profile_tree))\n",
    "\n",
    "file.close()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.20"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
